home *** CD-ROM | disk | FTP | other *** search
- /*
- Start-up-code for BareED
- Written 10 & 11/99 by Jörg van de Loo, Hövel 15, 47559 Kranenburg, FRG.
-
- 06 - 08/00 bug fixing, revising and adapting to GNU-C by Gunther Nikl.
- Thanks, Gunther.
-
- This code-code is freeware (written by J.v.d.Loo and Gunther Nikl) and
- it's especially written in mind the MaxonC++ compiler V4 and the GNU-C
- compiler V2.95.# (useful for C and ANSI-C and C++ modes).
-
- CLI parser 'borrowed' from AZTEC-C package (modified).
-
- This start-up-code set up argc and argv with the command line parameters
- given through the CLI and with all selected files given through
- Workbench, e.g. double click on project icon or shift select. Additional,
- it ensures the present of a 68020 processor and Kickstart 2.0 (beta).
-
- No stdin, stdout, stderr terminals will be set up since they are useless
- for BareED.
-
- The variables SysBase and ExecBase are only present within this
- start-up-code; this ensures that the additional code of BareED doesn't
- refer to the variables set up by the start-up-code. This is due to the
- fact that my compiler would otherwise address them as 32 bit addresses
- that will cause each time a reloc32 hunk entry. What I do is to refer to
- _DOS_Base and _Exec_Base once, to set up DOSBase and SysBase in the
- additional code of BareED, so that my compiler will them address from now
- on within BareED indirect to the processor register a4.
-
- When you compile this start-up-code ensure that no 68020 or higher
- instructions are used before the processor check is executed, otherwise
- on a plain 68000 processor this start-up-code will fail with a GURU.
-
- This start-up-code checks for the amount of free stack, too. Since this
- start-up-code uses very less stack (56 bytes when running and only up to
- 328 bytes while setting up variables) it should be enough when using a
- stack size of only 4096 bytes for the load file of BareED, even under 3rd
- party graphic emulation systems - which in fact do require much more
- stack than the native Amiga OS 3 graphic system (up to 3Kb when BareED is
- running).
-
- One goal of this start-up-code is, that it gains at lot of stack (up to
- 1.1 Kb) compared to the original start-up-code that comes along with my
- compiler (MaxonC++, Jörg).
- Currently it needs a given stack size of 4052 bytes when started off a
- CLI surround and 4046 bytes when started off the Workbench.
-
- 11/99 - modified this start-up-code to run-back when started off a CLI
- window or called with RunCommand() or within a batch script of an
- application.
-
- 06/00 - bug fixing and revising by Gunther Nikl.
- 07/00 - Can now be used for StormC; J.v.d.Loo
- 07/00 - Compliant to SAS-C; G. Nikl
- 08/00 - No forbid of task switching anymore, use of a semaphore lock, G. Nikl.
-
- NOTE: According to Gunther, GNU-C 2.95.# allows startup.c/BareED.c to be
- compiled in small (base relative) mode!
-
- I originally used the GNU-Compiler that can be found on Geek
- Gadgets II.
- Thanks to Mr. Fish for this compilation.
-
- And thank you Gunther for your annotations, although they first
- brought me down.
-
- This start-up code was bug fixed and improved by Gunther Nikl!
-
- Rework for MaconC++ and StormC done by J.v.d.Loo .
-
- NOTE: The handling of "__inline" in GNU-C / SAS-C does not complain
- with legal rules! Thus "__inline" has been removed for Storm
- and Maxon C/C++ compilers!
- */
-
-
- #include <exec/execbase.h>
- #include <exec/memory.h>
-
- #include <dos/dosextens.h>
- #include <dos/dostags.h>
-
- #include <workbench/startup.h>
-
- #include <clib/exec_protos.h>
- #include <clib/dos_protos.h>
-
- #if !defined(__MAXON__) && !defined(__STORM__)
- #include <proto/exec.h>
- #include <proto/dos.h>
- #else
- #include <pragma/exec_lib.h>
- #include <pragma/dos_lib.h>
- #endif
-
- #ifdef __GNUC__
- #define ASM
- #define REG(reg,arg) arg __asm(#reg)
- #else
- #if !defined (__MAXON__)
- #define ASM __asm
- #endif
- #define REG(reg,arg) register __##reg arg
- #endif
-
- #ifdef __MAXON__
- extern "C" void GetBaseReg( void);
- #define __saveds
- #define ASM
- #endif
-
- #if defined(__MAXON__) || defined(__STORM__)
- #define __inline
- #define __stdargs
- #ifdef __cplusplus // Tells if MAXON or STORM compilers run in C++ mode
- #define main main__UjPPUc // C++ name for main
- extern "ASM" main__UjPPUc( unsigned long argc, unsigned char **argv); // Real function name and parameters
- #pragma - // Turn off C++ compile mode (use ANSI-C instead)
- #endif
- #endif
-
- struct Message *WBenchMsg;
- struct ExecBase *_Exec_Base;
- struct DosLibrary *_DOS_Base;
-
- static struct ExecBase *SysBase; // Allow only to appear within startup.c
- static struct DosLibrary *DOSBase; // Allow only to appear within startup.c
-
- static BPTR _prg_dir = -1; // Initial value, no directory remembered
- static unsigned int _argc, _arg_len;
- static unsigned char **_argv, *_arg_lin;
- static struct Process *MasterTask = 0;
- static struct SignalSemaphore _AsyncSem;
- static short _Kick1;
-
-
- #define DOSName (UBYTE *)"dos.library"
- #define WrongCPU (UBYTE *)"ERROR: CPU < 68020!\n"
- #define NotEnoughtMem (UBYTE *)"ERROR: Not enought memory!\n"
- #define NotEnoughtStack (UBYTE *)"ERROR: Stack < 4096 bytes!\n"
- #define WrongLIB (UBYTE *)"ERROR: Library versions < 36!\n"
- #define WrongError (UBYTE *)"ERROR: Non-describt fault!\n" // ????
- #define Console "CON:0/0/320/80/ERROR REPORT/AUTO/CLOSE/WAIT"
-
-
- static void _main_jmp( unsigned long parlen, unsigned char *parameter);
- int __stdargs main( unsigned long argc, unsigned char **argv);
-
-
- // ################################################################
- /* Gunther has enhanced the start-up code so that it's now possible (when
- started off the CLI) that this routine is twice called: 1st time when
- initialising; 2nd time, when used as main entry for detached (new created)
- task! */
-
- int ASM __saveds INIT_0_run_me_at_first_place( REG(d0,unsigned long parlen), REG(a0,unsigned char *parameter) )
- {
- #ifdef __MAXON__ // Since MaxonC/C++ does not understand __saveds
- GetBaseReg();
- #endif
-
- _Exec_Base = SysBase = *((struct ExecBase **) 4); // From memory location 4 to exec library
-
- _main_jmp( parlen, parameter); // We only return here if the detach from the CLI was a success!
- // We jump to _main_jmp() because if we would do here
- // the following stuff, d0 and a0 would ever be pre-
- // reserved, which blows up the object file un-necessary
-
- return 0; // Return code (for CLI), in case detach was a success!
- }
-
- // ################################################################
-
- /* Following code is to avoid reloc32 entries - since they would otherwise
- (when linked with the object files) called via "jsr" and not via "bsr" */
-
- static __inline unsigned int strlenNR( register const unsigned char *str)
- {
- register unsigned int i = 0;
-
- while (*str++)
- i++;
- return i;
- }
-
- static __inline void strncpyNR( register unsigned char *d, register const unsigned char *s, register unsigned int i)
- {
- while (i)
- {
- *d++ = *s++;
- i--;
- }
- *d = 0;
- }
-
- static __inline void strcpyNR( register unsigned char *d, register const unsigned char *s)
- {
- while (*s)
- *d++ = *s++;
- *d = 0;
- }
-
- static __inline void strcatNR( register unsigned char *d, register const unsigned char *s)
- {
- while (*d)
- d++;
-
- while( *s)
- *d++ = *s++;
- *d = 0;
- }
-
- static __inline void strncatNR( register unsigned char *d, register const unsigned char *s, register unsigned int i)
- {
- while (*d)
- d++;
-
- while( i)
- {
- *d++ = *s++;
- i--;
- }
- *d = 0;
- }
-
- // ################################################################
-
- /* Function required by GNU-C linker, not necessary anymore - since
- all work already done by this start-up-code! */
-
- #ifdef __GNUC__
- void __main(void)
- {
- }
- #endif
-
- // ################################################################
-
- /* Taken without permission from the AZTEC-package, which in fact
- can be found in books and magazines, too */
-
- static void _cli_parse(struct Process *pp, unsigned long alen, register unsigned char *aptr)
- {
- register unsigned char *cp;
- register struct CommandLineInterface *cli;
- register unsigned char c;
-
- cli = (struct CommandLineInterface *) BADDR( pp->pr_CLI );
- cp = (unsigned char *) BADDR( cli->cli_CommandName );
-
- _arg_len = (unsigned char) cp[0] + alen + 2;
-
- if ( (_arg_lin = (unsigned char *) AllocMem( ((_arg_len + 7) & -8), MEMF_CLEAR ) ) == 0)
- return;
-
- c = cp[0];
- strncpyNR( _arg_lin, cp + 1, c);
- _arg_lin[c] = ' ';
- _arg_lin[c + 1] = 0;
- strncatNR( _arg_lin, aptr, alen);
- _arg_lin[c] = 0;
-
- for (_argc = 1, aptr = cp = _arg_lin + c + 1; ; _argc++)
- {
- while ( (c = *cp) == ' ' || c == '\t' || c == '\f' || c == '\r' || c == '\n')
- cp++;
- if (*cp < ' ')
- break;
- if (*cp == '"')
- {
- cp++;
- while ( (c = *cp++) )
- {
- *aptr++ = c;
- if (c == '"')
- {
- if (*cp == '"')
- {
- cp++;
- }
- else
- {
- aptr[-1] = 0;
- break;
- }
- }
- }
- }
- else
- {
- while ( (c = *cp++) && c != ' ' && c != '\t' && c != '\f' && c != '\r' && c != '\n')
- *aptr++ = c;
- *aptr++ = 0;
- }
- if (c == 0)
- --cp;
- }
-
- *aptr = 0;
- if ( (_argv = (unsigned char **) AllocMem( (((_argc + 1) * 4 + 7) & -8 ), MEMF_CLEAR ) ) == 0 )
- {
- _argc = 0;
- return;
- }
-
- for (c=0, cp=_arg_lin; c < _argc; c++)
- {
- _argv[c] = cp;
- cp += strlenNR( cp) + 1;
- }
-
- _argv[c] = 0;
- }
-
- // ################################################################
-
- /* Allows several Workbench passed in arguments for BareED */
-
- static void _wb_parse( struct WBStartup *msg)
- {
- int numargs, len, i;
- char str[256], *curr; // Normally I'm against arrays on stack, but since we're
- // at front of a load file it doesn't matter (because there
- // is enough free stack available) and we can here avoid
- // memory fragmentation through AllocMem()
-
- numargs = msg -> sm_NumArgs;
- _arg_len = (numargs + 2) * 4; // Number of arguments into amount bytes (plus 2 long words)
-
- /* Get length in bytes of all arguments including zero bytes and drawer terminators */
- i = 0;
- while (i < numargs)
- {
- if (msg -> sm_ArgList[i] . wa_Lock)
- NameFromLock( msg -> sm_ArgList[i] . wa_Lock, &str[0], 255);
- _arg_len += strlenNR( &str[0]);
- _arg_len += 2; // For zero byte and perhaps for drawer terminator "/" !
- _arg_len += strlenNR( msg -> sm_ArgList[i] . wa_Name);
- i++;
- }
-
- /* Allocate needed space for strings */
- _arg_lin = (unsigned char *) AllocMem( ((_arg_len + 7) & -8), MEMF_CLEAR );
- if ( !_arg_lin)
- return;
-
- _argc = numargs;
- _argv = (unsigned char **) _arg_lin;
- curr = _arg_lin + ((numargs + 2) * 4);
-
- /* Create and copy drawer and filenames into allocated memory; behind the argument pointers! */
- i = 0;
- while (i < numargs)
- {
- _argv[i] = curr;
- if (msg -> sm_ArgList[i] . wa_Lock)
- {
- NameFromLock( msg -> sm_ArgList[i] . wa_Lock, &str[0], 255);
- strcpyNR( curr, &str[0]);
- len = strlenNR( curr);
-
- if ( curr[ len - 1] != ':')
- {
- curr[len] = '/';
- len ++;
- }
- }
- else
- {
- len = 0;
- }
-
- strcpyNR( curr + len, msg -> sm_ArgList[i] . wa_Name);
- len = strlenNR( curr);
-
- curr += len + 1; // Behind zero byte
- i++; // Next arg
- }
-
- _prg_dir = CurrentDir( msg -> sm_ArgList[0] . wa_Lock); // Set up "progdir:"
- }
-
-
- // ################################################################
-
- /* Print error code down to a console window, if there isn't one yet,
- open one and give the message. */
-
- static __inline void GiveFault( int error)
- {
- if (_DOS_Base) // DOSBase set up?
- {
- static char console[] = Console;
- unsigned char *errorTxt = 0;
- BPTR newStdOut;
-
- if (_Kick1) // OS 2.0 ?
- console[27] = 0; // No, OS 1.x, so remove AUTO/CLOSE/WAIT from console description
-
- newStdOut = Open( WBenchMsg ? console : "*", MODE_NEWFILE); // Figure out if we have to open a console window
- if (newStdOut) // on our own - in case we've been fired up by Workbench
- {
- if (error == 105)
- errorTxt = WrongCPU;
- if (error == 122)
- errorTxt = WrongLIB;
- if (error == 217)
- errorTxt = NotEnoughtStack;
- if (error == 103)
- errorTxt = NotEnoughtMem;
- if (errorTxt == 0)
- errorTxt = WrongError; // ????
-
- Write( newStdOut, errorTxt, strlenNR( errorTxt) );
-
- if (WBenchMsg && _Kick1) // If WB-start and OS 1.x
- Delay( 5*60); // Wait a while
-
- Close( newStdOut); // Close console
- }
- }
- }
-
- // ################################################################
-
- /* Code to get program's return address and to call it */
-
- static const unsigned short _finally_code[] =
- {
- 0x2400, // move.l D0,D2 Save error code
- 0x93C9, // suba.l A1,A1
- 0x2C78,0x0004, // movea.l (4).w,A6
- 0x4EAE,0xFEDA, // jsr _LVOFindTask(A6) Own task
- 0x2040, // movea.l D0,A0
- 0x2068,0x00B0, // movea.l pr_ReturnAddr(A0),A0 Get system's exit address for us
- 0x4FE8,0xFFFC, // lea -4(A0),sp Restore initial stack
- 0x2002, // move.l D2,D0 Error code to D0
- 0x4E75 // rts Back to system
- };
-
- // ################################################################
-
- /* Since my compiler doesn't allow to overload a function (stub),
- I use a different name: exitNR ! */
-
- void __saveds exitNR( int error)
- {
- void (* ASM _finally)( REG(d0,int err) ) = (void (*ASM) ( REG( d0,int)) ) _finally_code;
-
- #ifdef __MAXON__
- GetBaseReg();
- #endif
-
- if (error)
- GiveFault( error);
-
- ((struct Process *) FindTask( NULL)) -> pr_Result2 = error;
-
- if (WBenchMsg)
- {
- if (_arg_lin)
- {
- FreeMem( _arg_lin, ((_arg_len + 7) & -8) );
- if (_prg_dir != -1) // If it's the same as initial, ignore
- CurrentDir( _prg_dir); // Else, lock of basic directory
- }
- Forbid();
- ReplyMsg( WBenchMsg);
- }
- else
- {
- if (_arg_lin)
- {
- FreeMem(_argv, (((_argc + 1) * 4 + 7) & -8) );
- FreeMem(_arg_lin, ((_arg_len + 7) & -8) );
- }
- }
-
- if (_DOS_Base)
- CloseLibrary( (struct Library *) _DOS_Base);
-
- _finally( error);
- }
-
- // ################################################################
-
- /* This function does not return to the caller ! */
-
- static void CallMainPRG( void)
- {
- exitNR( main( _argc, _argv));
- }
-
- // ################################################################
-
- /* Set up necessary things to provide an auto-detach (RunBack mode).
- This is a little tricky one, which cares about the priority and
- stack size of the original process, which is the setting for the
- new created.
-
- Enhanced by G. Nikl (06/00)
- */
-
- static APTR LaunchStartup( void)
- {
- struct CommandLineInterface *cli;
- struct Process *pr;
- unsigned char *cp;
- struct TagItem tag[5];
- char buf[256];
-
- pr = (struct Process *) FindTask( NULL);
-
- cli = (struct CommandLineInterface *) BADDR( pr->pr_CLI);
- cp = (unsigned char *) BADDR( cli->cli_CommandName);
-
- // Duplicate name: slave task's name equal to master's
- strncpyNR( buf, cp + 1, cp[0]);
-
- // Settings for task to create
- tag[0].ti_Tag = NP_Seglist;
- tag[0].ti_Data = (ULONG) cli->cli_Module;
- tag[1].ti_Tag = NP_FreeSeglist;
- tag[1].ti_Data = TRUE;
- tag[2].ti_Tag = NP_Name;
- tag[2].ti_Data = (ULONG) buf;
- tag[3].ti_Tag = NP_StackSize;
- tag[3].ti_Data = *(ULONG *) pr->pr_ReturnAddr;
- tag[4].ti_Tag = TAG_DONE;
-
- // 08/00 G. Nikl
- InitSemaphore(&_AsyncSem);
-
- // 08/00 G. Nikl
- ObtainSemaphore(&_AsyncSem);
-
- // Create slave task which becomes master task
- if ( (MasterTask = CreateNewProc( tag)) ) // Process created ?
- {
- cli->cli_Module = 0;
- }
-
- // 08/00 G. Nikl
- ReleaseSemaphore(&_AsyncSem);
-
- // Return whether success or not...
- return MasterTask;
- }
-
- // ################################################################
-
- /* This function can be twice called in case we've started from the
- CLI:
- First, when we fired up regularly through the user (MasterTask == 0);
- when we have called the second time (MasterTask != 0), we are the
- through LauchStartup() created task!
-
- Enhanced by G. Nikl (08/00)
- */
-
- static void _main_jmp( unsigned long parlen, unsigned char *parameter)
- {
- if ( !MasterTask) // First time fired up?
- {
- struct Process *proc = (struct Process *) FindTask( NULL);
- char *lower;
-
- if ( !proc -> pr_CLI)
- {
- WaitPort( &proc -> pr_MsgPort);
- WBenchMsg = GetMsg( &proc -> pr_MsgPort);
- }
- else
- {
- WBenchMsg = 0;
- }
-
- // Lower Kickstart 2.0 (beta)?
- _Kick1 = _Exec_Base -> LibNode . lib_Version < 36 ? TRUE : FALSE;
-
- _DOS_Base = DOSBase = (struct DosLibrary *) OpenLibrary( DOSName, 33);
- if ( !_DOS_Base || _Kick1)
- exitNR( 122);
-
- if ( !(_Exec_Base -> AttnFlags & AFF_68020)) // At least a 68020 processor?
- exitNR( 105);
-
- // Do we have at least 4000 bytes of stack available? - NOTE: the variable proc is on
- // the top of the stack so using this address is the current upper bound of the stack!
- lower = proc->pr_Task.tc_SPLower;
- if ( (proc->pr_Task.tc_Node.ln_Type == NT_PROCESS) && proc->pr_CLI)
- lower = (char *) proc->pr_ReturnAddr + sizeof( ULONG) - *(ULONG *) proc->pr_ReturnAddr;
- if ( ((char *) &proc - lower) < 4000)
- exitNR( 217);
-
- if (WBenchMsg)
- {
- _wb_parse( (struct WBStartup *) WBenchMsg);
- if ( !_arg_lin)
- exitNR( 103); // Fail: no mem
- }
- else
- {
- _cli_parse( proc, parlen, parameter);
- if ( !_arg_lin)
- exitNR( 103);
- }
-
- // Try detach from CLI, if it fails, go on normal
- if (WBenchMsg || !LaunchStartup())
- {
- CallMainPRG();
- }
- }
- else // ...we're the task that detached off CLI!
- {
- // Wait for parent
- ObtainSemaphore(&_AsyncSem);
- // Release lock
- ReleaseSemaphore(&_AsyncSem);
- // Main loop
- CallMainPRG();
- }
- }
-
- // ################################################################
-